﻿// Original author contact info: Owen Emlen (owene_1998@yahoo.com)
// Note: other individuals may also have contributed to this code
// Project hosted on CodePlex.com as of 1/10/2009 at http://www.codeplex.com/EmlenMud
using System.Runtime.Serialization;
using System;
using System.Collections.Generic;
using System.Collections;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
#if USE_HYPER
using Hyper.ComponentModel;
#endif

namespace BrainTechLLC.ThreadSafeObjects
{
    /// <summary>
    /// A simple queue that avoids using lock().  Fast/efficient to dequeue/enqueue from multiple threads
    /// </summary>	
#if NO_SILVERLIGHT
    [TypeDescriptionProvider(typeof(HyperTypeDescriptionProvider))]
#endif
    [Serializable]
    [DataContract]
    public class ThreadSafeQueue<TListType> : Lockable, ICollection, INotifyCollectionChanged,
        IMultipleItems<TListType>, ISupportsCount, ISupportsEnqueueDequeue<TListType> where TListType : class
    {
        public override string ToString()
        {
            return _count.ToString();
        }

        [DataMember]
        public Queue<TListType> _queue = new Queue<TListType>();
        [DataMember]
        public int _count;

        public List<TListType> AllItems { get { return new List<TListType>(ArrayOfItems); } }
        public int Count { get { return _count; } }

        public int Enqueue(TListType item)
        {
            // We need to be sure that no other threads simultaneously modify the shared _queue
            // object during our enqueue operation
            AquireLock();
            {
                _queue.Enqueue(item);
                _count++;
            }
            ReleaseLock();

            OnNotifyCollectionChanged(NotifyCollectionChangedAction.Add, item);
            return _count;
        }

        public void EnqueueMultiple(List<TListType> items)
        {
            AquireLock();
            {
                foreach (TListType item in items)
                    _queue.Enqueue(item);

                _count += items.Count;
            }
            ReleaseLock();

            OnNotifyCollectionChanged(NotifyCollectionChangedAction.Add, items);
        }

        public int DequeueMultiple(List<TListType> items, int maxItems)
        {
            int dequeued = 0;

            AquireLock();
            {
                while (_count > 0 && dequeued < maxItems)
                {
                    TListType item = _queue.Dequeue();
                    items.Add(item);
                    _count--;
                    dequeued++;
                }
            }
            ReleaseLock();

            if (CollectionChanged != null)
                OnNotifyCollectionChanged(NotifyCollectionChangedAction.Remove, items);

            return dequeued;
        }

        public int DequeueMultiple(TListType[] items, int maxItems)
        {
            int dequeued = 0;

            AquireLock();
            {
                while (_count > 0 && dequeued < maxItems)
                {
                    TListType item = _queue.Dequeue();
                    items[dequeued] = item;
                    dequeued++;
                    _count--;
                }
            }
            ReleaseLock();

            if (CollectionChanged != null)
                OnNotifyCollectionChanged(NotifyCollectionChangedAction.Remove, new List<TListType>(items));

            return dequeued;
        }

        public TListType DequeueItem()
        {
            TListType found;
            if (Dequeue(out found))
            {
                OnNotifyCollectionChanged(NotifyCollectionChangedAction.Remove, found);
                return found;
            }
            return null;
        }

        public bool Dequeue(out TListType item)
        {
            item = null;

            // We need to be sure that no other threads simultaneously modify the shared _queue
            // object during our dequeue operation
            AquireLock();
            {
                if (_count > 0)
                {
                    item = _queue.Dequeue();
                    _count--;
                }
            }
            ReleaseLock();

            if (item != null)
                OnNotifyCollectionChanged(NotifyCollectionChangedAction.Remove, item);

            return (item != null);
        }

        public bool IsInList(TListType item)
        {
            bool found;

            AquireLock();
            {
                found = _queue.Contains(item);
            }
            ReleaseLock();

            return found;
        }

        public TListType[] ArrayOfItems
        {
            get
            {
                TListType[] list;

                AquireLock();
                {
                    list = _queue.ToArray();
                }
                ReleaseLock();

                return list;
            }
        }

        public virtual void Clear()
        {
            IList<TListType> items = null;

            if (CollectionChanged != null)
                items = new List<TListType>(AllItems);

            AquireLock();
            {
                _queue.Clear();
                _count = 0;
            }
            ReleaseLock();

            OnNotifyCollectionChanged(NotifyCollectionChangedAction.Reset, items);
        }

        public void OnNotifyCollectionChanged(NotifyCollectionChangedAction action, IList<TListType> changedItems)
        {
            if (CollectionChanged != null)
            {
                switch (action)
                {
                    case NotifyCollectionChangedAction.Add:
                        CollectionChanged(this, new NotifyCollectionChangedEventArgsEx<TListType>(action, changedItems));
                        break;
                    case NotifyCollectionChangedAction.Remove:
                        CollectionChanged(this, new NotifyCollectionChangedEventArgsEx<TListType>(action, changedItems));
                        break;
                    case NotifyCollectionChangedAction.Replace:
                        CollectionChanged(this, new NotifyCollectionChangedEventArgsEx<TListType>(action, changedItems[0], changedItems[1]));
                        break;
                    case NotifyCollectionChangedAction.Reset:
                        CollectionChanged(this, new NotifyCollectionChangedEventArgsEx<TListType>(action));
                        break;
                }
            }
        }

        public void OnNotifyCollectionChanged(NotifyCollectionChangedAction action, TListType changedItem)
        {
            if (CollectionChanged != null)
                OnNotifyCollectionChanged(action, new List<TListType>() { changedItem });
        }

        #region INotifyCollectionChanged Members

#if NO_SILVERLIGHT
        [field: NonSerialized]
        public event NotifyCollectionChangedEventHandler CollectionChanged;
#else
        [field: NonSerialized]
        public event EventHandler<NotifyCollectionChangedEventArgsEx<TListType>> CollectionChanged;
#endif

        #endregion

        #region ICollection Members

        public void CopyTo(Array array, int index)
        {
            _queue.CopyTo((TListType[])array, index);
        }

        public bool IsSynchronized
        {
            get { return true; }
        }

        public object SyncRoot
        {
            get { return this; }
        }

        #endregion

        #region IEnumerable Members

        public IEnumerator GetEnumerator()
        {
            return _queue.GetEnumerator();
        }

        #endregion
    }
}